RSPEED-2809: Reduce infer_endpoint cyclomatic complexity#1463
Conversation
WalkthroughRefactored infer endpoint flow: added Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant InferEndpoint as InferEndpoint/_handler
participant LLMHelper as _call_llm
participant OpenAI
participant Builder as _build_infer_response
Client->>InferEndpoint: POST /infer (infer_request)
InferEndpoint->>LLMHelper: await _call_llm(infer_request, resolved_model_id)
LLMHelper->>OpenAI: client.responses.create(...)
OpenAI-->>LLMHelper: OpenAIResponseObject
LLMHelper-->>InferEndpoint: OpenAIResponseObject
InferEndpoint->>Builder: _build_infer_response(response or None, include_metadata, ...)
Builder-->>InferEndpoint: formatted response (verbose or minimal)
InferEndpoint-->>Client: HTTP response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/endpoints/rlsapi_v1.py (1)
567-568:⚠️ Potential issue | 🟡 MinorOverly restrictive condition for token usage extraction in error handler.
The condition
verbose_enabled and response is not Nonemeans token usage won't be extracted in error cases when verbose mode is disabled. If a response was received before an exception occurred, token usage should be recorded regardless of verbose mode to maintain accurate metrics.🔧 Proposed fix
except _INFER_HANDLED_EXCEPTIONS as error: - if verbose_enabled and response is not None: - extract_token_usage(response.usage, model_id) # type: ignore[arg-type] + if response is not None: + extract_token_usage(response.usage, model_id) # type: ignore[arg-type] _record_inference_failure(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/endpoints/rlsapi_v1.py` around lines 567 - 568, The current check `if verbose_enabled and response is not None:` prevents extracting token usage when an API response exists but verbose is false; change the logic so that extract_token_usage(response.usage, model_id) is invoked whenever a response with usage was received (i.e., if response is not None and response.usage is present) regardless of verbose_enabled, while keeping any verbose-only logging guarded by verbose_enabled; update the code around the error-handler block that references verbose_enabled, response and calls extract_token_usage to first check response (and response.usage) and then call extract_token_usage, moving it out of the verbose-only branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/endpoints/rlsapi_v1.py`:
- Around line 266-271: The tools parameter of _call_llm currently uses the Any
type; replace it with the concrete MCP tool type (or a module-level type alias)
to satisfy typing rules—import the MCP tool interface/TypedDict/Protocol if
available and change the signature tools: Optional[list[Any]] to tools:
Optional[list[McpToolType]] (or define McpToolType = ... at top of the module
and use that) and update any internal usages to that type to ensure correct
static typing for the _call_llm function.
- Around line 558-564: In infer_endpoint, after calling _call_llm and assigning
response (the same way retrieve_simple_response does), call
extract_token_usage(response.usage, resolved_model_id) on the successful path so
token metrics are recorded; locate the response variable returned from _call_llm
in infer_endpoint and add the extract_token_usage call before proceeding to
build response_text (mirroring retrieve_simple_response's usage extraction),
leaving the existing exception-path extract_token_usage intact.
---
Outside diff comments:
In `@src/app/endpoints/rlsapi_v1.py`:
- Around line 567-568: The current check `if verbose_enabled and response is not
None:` prevents extracting token usage when an API response exists but verbose
is false; change the logic so that extract_token_usage(response.usage, model_id)
is invoked whenever a response with usage was received (i.e., if response is not
None and response.usage is present) regardless of verbose_enabled, while keeping
any verbose-only logging guarded by verbose_enabled; update the code around the
error-handler block that references verbose_enabled, response and calls
extract_token_usage to first check response (and response.usage) and then call
extract_token_usage, moving it out of the verbose-only branch.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: be74eecd-062d-4255-a72f-c7dceecbcdfa
📒 Files selected for processing (1)
src/app/endpoints/rlsapi_v1.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build-pr
- GitHub Check: E2E: library mode / ci
- GitHub Check: E2E: server mode / ci
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Use absolute imports for internal modules (e.g.,from authentication import get_auth_dependency)
Usefrom llama_stack_client import AsyncLlamaStackClientfor Llama Stack imports
Checkconstants.pyfor shared constants before defining new ones
All modules start with descriptive docstrings explaining purpose
Uselogger = get_logger(__name__)fromlog.pyfor module logging
Type aliases defined at module level for clarity
All functions require docstrings with brief descriptions
Use complete type annotations for function parameters and return types
Use union types with modern syntax:str | intinstead ofUnion[str, int]
UseOptional[Type]for optional types in type annotations
Use snake_case with descriptive, action-oriented names for functions (get_, validate_, check_)
Avoid in-place parameter modification anti-patterns: return new data structures instead of modifying parameters
Useasync deffor I/O operations and external API calls
HandleAPIConnectionErrorfrom Llama Stack in error handling
Uselogger.debug()for detailed diagnostic information
Uselogger.info()for general information about program execution
Uselogger.warning()for unexpected events or potential problems
Uselogger.error()for serious problems that prevented function execution
All classes require descriptive docstrings explaining purpose
Use PascalCase for class names with descriptive names and standard suffixes:Configuration,Error/Exception,Resolver,Interface
Use complete type annotations for all class attributes; avoid usingAny
Follow Google Python docstring conventions for all modules, classes, and functions
IncludeParameters:,Returns:,Raises:sections in function docstrings as needed
Files:
src/app/endpoints/rlsapi_v1.py
src/app/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/app/**/*.py: Usefrom fastapi import APIRouter, HTTPException, Request, status, Dependsfor FastAPI dependencies
Use FastAPIHTTPExceptionwith appropriate status codes for API endpoint error handling
Files:
src/app/endpoints/rlsapi_v1.py
🔇 Additional comments (4)
src/app/endpoints/rlsapi_v1.py (4)
256-263: LGTM - Token usage extraction properly preserved.The refactor correctly maintains token usage extraction and text extraction as caller responsibilities after delegating to
_call_llm().
386-403: LGTM - Clean extraction of verbose mode check.The three-way check is correctly encapsulated with clear documentation explaining the dual opt-in requirement.
406-456: LGTM - Response construction logic is well-structured.The function correctly bifurcates between verbose (with
build_turn_summarymetadata) and minimal responses based on presence of the response object.
604-609: LGTM - Clean conditional response passing.Using
response if verbose_enabled else Noneis an elegant way to signal verbose mode to_build_infer_response, aligning with its documented behavior.
23cc2cc to
d4ea82f
Compare
|
Please rebase, "branching" images were introduced in different PR. |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
Extract helpers from infer_endpoint to eliminate the verbose mode branching that inflated its complexity: - _call_llm: transport-only LLM call (no metrics side effects) - _is_verbose_enabled: the 3-way config+request check - _build_infer_response: verbose vs minimal response construction, keyed on response object presence rather than a boolean flag retrieve_simple_response now delegates to _call_llm internally and handles its own token usage extraction. The verbose failure path in infer_endpoint is preserved: if the LLM call succeeded but later processing fails, token usage is still recorded. No behavior changes, pure refactor. Signed-off-by: Major Hayden <major@redhat.com>
d4ea82f to
b180204
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/endpoints/rlsapi_v1.py`:
- Around line 633-634: The code unconditionally calls
extract_token_usage(response.usage, model_id) which causes double-counting when
the verbose-success path later invokes _build_infer_response() and
build_turn_summary() that also record usage; modify the call site so
extract_token_usage is only executed when not using the verbose/summary path
(e.g., guard the call with the same verbose flag used by _build_infer_response
or skip it when build_turn_summary() will run), ensuring only one of
extract_token_usage or build_turn_summary records token usage for a given
request.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9d052c01-1b9f-43a7-b12c-ea42a9443040
📒 Files selected for processing (1)
src/app/endpoints/rlsapi_v1.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build-pr
- GitHub Check: E2E: library mode / ci
- GitHub Check: E2E Tests for Lightspeed Evaluation job
- GitHub Check: E2E: server mode / ci
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Use absolute imports for internal modules (e.g.,from authentication import get_auth_dependency)
Usefrom llama_stack_client import AsyncLlamaStackClientfor Llama Stack imports
Checkconstants.pyfor shared constants before defining new ones
All modules start with descriptive docstrings explaining purpose
Uselogger = get_logger(__name__)fromlog.pyfor module logging
Type aliases defined at module level for clarity
All functions require docstrings with brief descriptions
Use complete type annotations for function parameters and return types
Use union types with modern syntax:str | intinstead ofUnion[str, int]
UseOptional[Type]for optional types in type annotations
Use snake_case with descriptive, action-oriented names for functions (get_, validate_, check_)
Avoid in-place parameter modification anti-patterns: return new data structures instead of modifying parameters
Useasync deffor I/O operations and external API calls
HandleAPIConnectionErrorfrom Llama Stack in error handling
Uselogger.debug()for detailed diagnostic information
Uselogger.info()for general information about program execution
Uselogger.warning()for unexpected events or potential problems
Uselogger.error()for serious problems that prevented function execution
All classes require descriptive docstrings explaining purpose
Use PascalCase for class names with descriptive names and standard suffixes:Configuration,Error/Exception,Resolver,Interface
Use complete type annotations for all class attributes; avoid usingAny
Follow Google Python docstring conventions for all modules, classes, and functions
IncludeParameters:,Returns:,Raises:sections in function docstrings as needed
Files:
src/app/endpoints/rlsapi_v1.py
src/app/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/app/**/*.py: Usefrom fastapi import APIRouter, HTTPException, Request, status, Dependsfor FastAPI dependencies
Use FastAPIHTTPExceptionwith appropriate status codes for API endpoint error handling
Files:
src/app/endpoints/rlsapi_v1.py
🧠 Learnings (4)
📚 Learning: 2026-04-06T20:18:07.852Z
Learnt from: major
Repo: lightspeed-core/lightspeed-stack PR: 1463
File: src/app/endpoints/rlsapi_v1.py:266-271
Timestamp: 2026-04-06T20:18:07.852Z
Learning: In the lightspeed-stack codebase, within `src/app/endpoints/` inference/MCP endpoints, treat `tools: Optional[list[Any]]` in MCP tool definitions as an intentional, consistent typing pattern (used across `query`, `responses`, `streaming_query`, `rlsapi_v1`). Do not raise or suggest this as a typing issue during code review; changing it in isolation could break endpoint typing consistency across the codebase.
Applied to files:
src/app/endpoints/rlsapi_v1.py
📚 Learning: 2026-04-05T12:19:36.009Z
Learnt from: CR
Repo: lightspeed-core/lightspeed-stack PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-05T12:19:36.009Z
Learning: Applies to src/**/*.py : Use complete type annotations for all class attributes; avoid using `Any`
Applied to files:
src/app/endpoints/rlsapi_v1.py
📚 Learning: 2026-02-23T14:57:07.030Z
Learnt from: asimurka
Repo: lightspeed-core/lightspeed-stack PR: 1198
File: src/utils/responses.py:184-192
Timestamp: 2026-02-23T14:57:07.030Z
Learning: In the lightspeed-stack codebase, duplicate `client.models.list()` calls in model selection flows (e.g., in `src/utils/responses.py` prepare_responses_params) are acceptable as relatively cheap operations; avoiding such duplication should not introduce unnecessary complexity to the flow.
Applied to files:
src/app/endpoints/rlsapi_v1.py
📚 Learning: 2026-02-25T07:46:39.608Z
Learnt from: asimurka
Repo: lightspeed-core/lightspeed-stack PR: 1211
File: src/models/responses.py:8-16
Timestamp: 2026-02-25T07:46:39.608Z
Learning: In the lightspeed-stack codebase, src/models/requests.py uses OpenAIResponseInputTool as Tool while src/models/responses.py uses OpenAIResponseTool as Tool. This type difference is intentional - input tools and output/response tools have different schemas in llama-stack-api.
Applied to files:
src/app/endpoints/rlsapi_v1.py
🔇 Additional comments (1)
src/app/endpoints/rlsapi_v1.py (1)
267-304: Centralizing the raw LLM transport here looks good.This helper now owns client lookup and
responses.create(...), so the callers no longer depend on an outer-scopeclientand can stay focused on post-processing.
Description
Extract helpers from
infer_endpointto reduce its cyclomatic complexity from C(13) to B(7). Pure refactor, no behavior changes.The verbose mode branching was the main contributor: the same
verbose_enabledcheck appeared in four places (config check, inference fork, failure compensation, response building). This PR consolidates those into focused helpers:_call_llm(): Transport-only LLM call. ReturnsOpenAIResponseObjectwithout metrics side effects. Callers handle token usage extraction._is_verbose_enabled(): The 3-wayconfiguration.customization is not None and allow_verbose_infer and include_metadatacheck._build_infer_response(): Builds the final response. Keyed onresponseobject presence (not a separate boolean), soresponse=Nonemeans minimal,response=<object>means verbose with metadata.retrieve_simple_response()now delegates to_call_llm()and handles its ownextract_token_usage()call, preserving the existing public API.Complexity before/after
infer_endpoint_call_llm_is_verbose_enabled_build_infer_responseretrieve_simple_responseType of change
Tools used to create PR
Related Tickets & Documents
Checklist before requesting a review
Testing
uv run make format && uv run make verify- all linters pass cleanuv run pytest tests/unit/app/endpoints/test_rlsapi_v1.py tests/integration/endpoints/test_rlsapi_v1_integration.py -v- all 60 tests pass (43 unit + 17 integration)uv run radon cc src/app/endpoints/rlsapi_v1.py -s -a- no function above B, file average A(3.4)retrieve_simple_response, verbose viabuild_turn_summaryin_build_infer_response)Summary by CodeRabbit